﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Threading;

namespace AlternativePorts.Tests
{
	[TestFixture]
	public class CausalityTests
	{
		Port<Exception> errorPort;
		Port<int> coordinationPort;

		[SetUp]
		public void SetUp()
		{
			errorPort = new Port<Exception>();
			coordinationPort = new Port<int>();
		}

		[Test]
		public void InstallCausality()
		{
			Assert.IsNull(Causality.Current);
			using (new Causality(errorPort, coordinationPort))
			{
				Assert.IsNotNull(Causality.Current);
				Assert.AreSame(errorPort, Causality.Current.ErrorPort);
				Assert.AreSame(coordinationPort, Causality.Current.CoordinationPort);
			}
			Assert.IsNull(Causality.Current);
		}

		[Test]
		public void PostWithCausality_NoException()
		{
			var port = new Port<int>();
			int item = 0;
			port.RegisterReceiver(i => item = i, ReceiveMode.Parallel);
			using (new Causality(errorPort, coordinationPort))
			{
				port.Post(123);
			}

			Thread.Sleep(100);

			Assert.AreEqual(123, item);
			Assert.AreEqual(0, errorPort.ItemCount);
		}

		[Test]
		public void PostWithCausality_ExceptionThrown()
		{
			var port = new Port<int>();
			var ex = new Exception();
			port.RegisterReceiver(i => { throw ex; }, ReceiveMode.Parallel);
			using (new Causality(errorPort, coordinationPort))
			{
				port.Post(123);
			}

			Thread.Sleep(100);

			Assert.AreEqual(1, errorPort.ItemCount);
			Assert.AreSame(ex, errorPort.ReceiveSync());
		}

		[Test]
		public void PostWithCausality_SuccessivePorts_ExceptionThrown()
		{
			var port1 = new Port<int>();
			var port2 = new Port<int>();
			var ex = new Exception();
			port1.RegisterReceiver(i => port2.Post(i), ReceiveMode.Parallel);
			port2.RegisterReceiver(i => { throw ex; }, ReceiveMode.Parallel);
			using (new Causality(errorPort, coordinationPort))
			{
				port1.Post(123);
			}

			Thread.Sleep(100);

			Assert.AreEqual(0, port1.ItemCount);
			Assert.AreEqual(0, port2.ItemCount);
			Assert.AreEqual(1, errorPort.ItemCount);
			Assert.AreSame(ex, errorPort.ReceiveSync());
		}

		[Test]
		public void PostWithCausality_CoordinationPort()
		{
			var port = new Port<int>();
			var ex = new Exception();
			port.RegisterReceiver(i => Causality.Current.CoordinationPort.PostUnknownType(i), ReceiveMode.Parallel);
			using (new Causality(errorPort, coordinationPort))
			{
				port.Post(123);
			}

			Thread.Sleep(100);

			Assert.AreEqual(1, coordinationPort.ItemCount);
			Assert.AreEqual(123, coordinationPort.ReceiveSync());
		}
	}
}
